package com.lcc.lynx.common.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * @author lcc
 * @version 1.0.0.0
 * Description: Pojo工具类
 * DateTime: 2017-12-05 17:39
 * Copyright: Copyright (c) 2017-2027
 */
public class PojoUtil {
    /**
     * POJO对象间属性Copy<br><i>非静态变量</i>
     *
     * @param s                 源对象
     * @param d                 目标对象
     * @param excludeFieldNames 排除在外的字段
     * @return 目标对象
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static <S, D> D copyPropertys(S s, D d, String... excludeFieldNames) {
        if (s == null || d == null) {
            return d;
        }

        // 特定参数列表
        List<String> excludeFieldList = paramList(excludeFieldNames);

        Class scls = s.getClass();
        Class dcls = d.getClass();
        Set<Field> sfields = getAllFields(scls);
        Set<Field> dfields = getAllFields(dcls);
        try {
            for (Field dfield : dfields) {
                boolean exits = false;
                for (Field sfield : sfields) {
                    if (sfield.getName().equals(dfield.getName()) && sfield.getType().equals(dfield.getType())) {
                        exits = true;
                        break;
                    }
                }
                if (!exits) {
                    continue;
                }
                if (Modifier.isStatic(dfield.getModifiers())) {
                    continue; // 静态变量 则跳出
                }
                // 目标：属性
                String dName = dfield.getName();
                if (excludeFieldList.contains(dName)) {
                    continue;
                }
                // 目标：属性类型
                Class dType = dfield.getType();
                String dfieldName = dName.substring(0, 1).toUpperCase() + dName.substring(1);
                Method sGetMethod = null;
                try {
                    sGetMethod = scls.getMethod("get" + dfieldName);
                } catch (NoSuchMethodException nsmEx) {
                    try {
                        sGetMethod = scls.getMethod("get" + dName);
                    } catch (NoSuchMethodException e) {
                        continue;
                    }
                }
                // 源：值
                Object value = sGetMethod.invoke(s);

                // 目标：赋值
                Method dSetMethod = null;
                try {
                    dSetMethod = dcls.getMethod("set" + dfieldName, dType);
                } catch (NoSuchMethodException nsmEx) {
                    try {
                        dSetMethod = dcls.getMethod("set" + dName, dType);
                    } catch (NoSuchMethodException e) {
                        continue; // 没有get方法 则跳出
                    }
                }
                dSetMethod.invoke(d, value);
            }
        } catch (SecurityException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return d;
    }

    /**
     * 获取类所有属性（含父类继承的属性）
     *
     * @param clz
     * @return
     */
    private static Set<Field> getAllFields(Class<?> clz) {
        Set<Field> set = new HashSet<Field>();
        if (clz.equals(Object.class)) {
            return set;
        }
        for (Field f : clz.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers())) {
                continue; // 踢除 静态变量
            }
            set.add(f);
        }
        set.addAll(getAllFields(clz.getSuperclass()));
        return set;
    }

    /**
     * 参数列表转换
     *
     * @param excludeParamNames
     * @return List<String>
     */
    private static List<String> paramList(String... excludeParamNames) {
        List<String> exParams = new ArrayList<String>();
        if (excludeParamNames != null && excludeParamNames.length > 0) {
            for (String excludeParamName : excludeParamNames) {
                if(excludeParamName.indexOf(",") > 0){
                    String[] params = excludeParamName.split(",");
                    exParams.addAll(Arrays.asList(params));
                } else {
                    exParams.add(excludeParamName);
                }

            }
        }
        return exParams;
    }
}
